package com.apache.tools;

import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 * 对文件/目录进行压缩处理，对压缩文件进行解压处理
 */
public class FileZipUtil {
	
	/**
	 * 压缩文件/目录，
	 * 参数传递例子【压缩目录："d:\\test\\","d\\test.zip";压缩文件："d:\\test\ss.xls","d:\\test\\ss.zip"】
	 * @param srcFilePathName 要压缩的文件/目录的绝对路径
	 * @param zipFilePathName 指定压缩后文件路径和名称
	 * @return 返回压缩文件大小
	 */
	public static Map<String,String> fileToZip(String srcFilePathName, String zipFilePathName){
		Map<String,String> resultMap = new HashMap<String,String>();
		long zipSize=0;
		try {
			resultMap.put("fileNameOld",srcFilePathName);
			if (!StrUtil.isNull(srcFilePathName)&&!StrUtil.isNull(zipFilePathName)) {
				File sourceFile = new File(srcFilePathName);
				List<File> fileList = new ArrayList<File>();
				boolean isDir=true;
				if (sourceFile.isDirectory()) {
					fileList = getSubFiles(sourceFile);
					isDir=true;
				}else if (sourceFile.isFile()) {
					fileList.add(sourceFile);
					isDir=false;
				}
				//压缩文件对象
				ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFilePathName));
				ZipEntry ze = null;
				byte[] buf = new byte[1024];
				int readLen = 0;
				for (int i = 0; i < fileList.size(); i++) {
					File file = (File) fileList.get(i);
					if (!file.exists()) {
						continue;
					}
					if (isDir) {
						/**
						 * 创建一个ZipEntry，并设置Name和其它的一些属性
						 * 注意，该ZipEntry是包含目录结构的，此功能是由getAbsFileName(String,string)完成的
						 */
						ze = new ZipEntry(getAbsFileName(srcFilePathName, file));
					}else{
						/**
						 * 如果是文件就直接创建
						 */
						ze = new ZipEntry(sourceFile.getName());
					}
					ze.setSize(file.length());
					ze.setTime(file.lastModified());

					/***************将ZipEntry加到zos中，再写入实际的文件内容************************/
					zos.putNextEntry(ze);
					InputStream is = new BufferedInputStream(new FileInputStream(file));
					while ((readLen = is.read(buf, 0, 1024)) != -1) {
						zos.write(buf, 0, readLen);
					}
					is.close();
				}
				zos.close();
				resultMap.put("fileCount",String.valueOf(fileList.size()));
				resultMap.put("fileNameNew",zipFilePathName);
				File zipFile = new File(zipFilePathName);
				resultMap.put("zipName",zipFile.getName());
				zipSize=zipFile.length();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			zipSize=0;
		} catch (IOException e) {
			e.printStackTrace();
			zipSize=0;
		}

		resultMap.put("fileSize",getPrintSize(zipSize));

		return resultMap;
	}
	private static String getPrintSize(long size) {
		//如果字节数少于1024，则直接以B为单位，否则先除于1024，后3位因太少无意义
		if (size < 1024) {
			return String.valueOf(size) + "B";
		} else {
			size = size / 1024;
		}
		//如果原字节数除于1024之后，少于1024，则可以直接以KB作为单位
		//因为还没有到达要使用另一个单位的时候
		//接下去以此类推
		if (size < 1024) {
			return String.valueOf(size) + "KB";
		} else {
			size = size / 1024;
		}
		if (size < 1024) {
			//因为如果以MB为单位的话，要保留最后1位小数，
			//因此，把此数乘以100之后再取余
			size = size * 100;
			return String.valueOf((size / 100)) + "."
					+ String.valueOf((size % 100)) + "MB";
		} else {
			//否则如果要以GB为单位的，先除于1024再作同样的处理
			size = size * 100 / 1024;
			return String.valueOf((size / 100)) + "."
					+ String.valueOf((size % 100)) + "GB";
		}
	}
	/**
	 * 解压文件
	 * 参数传递例子【"d:\\newfile\\test.zip",""d:\\newfile\\test"】
	 * @param zipFilePathName 压缩文件路径和名称
	 * @param outFilePathName 解压后目录名称
	 * @return 返回解压后目录的路径
	 */
	public static Map<String,String> zipToFile(String zipFilePathName, String outFilePathName){
		Map<String,String> resultMap = new HashMap<String,String>();
		String filePath="";
		try {
			ZipFile zfile = new ZipFile(zipFilePathName);
			resultMap.put("fileNameOld",zfile.getName());
			Enumeration zList = zfile.entries();
			ZipEntry ze = null;
			byte[] buf = new byte[1024];
			while (zList.hasMoreElements()) {
				// 从ZipFile中得到一个ZipEntry
				ze = (ZipEntry) zList.nextElement();
				if (ze.isDirectory()) {
					continue;
				}
				// 以ZipEntry为参数得到一个InputStream，并写到OutputStream中
				InputStream is = new BufferedInputStream(zfile.getInputStream(ze));
				OutputStream os = new BufferedOutputStream(new FileOutputStream(getRealFileName(outFilePathName, ze.getName())));
				int readLen = 0;
				while ((readLen = is.read(buf, 0, 1024)) != -1) {
					os.write(buf, 0, readLen);
				}
				is.close();
				os.close();
			}
			zfile.close();
			filePath=outFilePathName;
			resultMap.put("fileNameNew",filePath);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return resultMap;
	}

	/**
	 * 取得指定目录以及其各级子目录下的所有文件的列表，注意，返回的列表中不含目录.
	 * @param baseDir File 指定的目录
	 * @return 包含java.io.File的List
	 */
	private static List<File> getSubFiles(File baseDir) {
		List<File> fileList = new ArrayList<File>();
		File[] tmp = baseDir.listFiles();
		for (int i = 0; i < tmp.length; i++) {
			// 如果是文件，直接加入fileList
			if (tmp[i].isFile()) {
				fileList.add(tmp[i]);
			}
			// 如果是目录，递归调用本方法，并将结果集加入fileList
			if (tmp[i].isDirectory()) {
				fileList.addAll(getSubFiles(tmp[i]));
			}
		}
		return fileList;
	}

	/**
	 * 给定根目录，返回一个相对路径所对应的实际文件名.
	 * @param baseDir 指定根目录
	 * @param absFileName 相对路径名，来自于ZipEntry中的name
	 * @return java.io.File 实际的文件
	 */
	private static File getRealFileName(String baseDir, String absFileName) {
		String[] dirs = absFileName.split("/");
		File ret = new File(baseDir);
		// 创建目录结构
		if (dirs.length > 1) {
			for (int i = 0; i < dirs.length - 1; i++) {
				ret = new File(ret, dirs[i]);
			}
		}
		if (!ret.exists()) {
			ret.mkdirs();
		}
		ret = new File(ret, dirs[dirs.length - 1]);
		return ret;
	}
	

	/**
	 * 给定根目录及文件的实际路径，返回带有相对路径的文件名，用于zip文件中的路径.
	 * 如将绝对路径，baseDir\dir1\dir2\file.txt改成 dir1/dir2/file.txt
	 * @param baseDir java.lang.String 根目录
	 * @param realFileName java.io.File 实际的文件名
	 * @return 相对文件名
	 */
	private static String getAbsFileName(String baseDir, File realFileName) {
		File real = realFileName;
		File base = new File(baseDir);
		String ret = real.getName();

		while (true) {
			real = real.getParentFile();
			if (real == null)
				break;
			if (real.equals(base))
				break;
			else {
				ret = real.getName() + "/" + ret;
			}
		}
		return ret;
	}

	public static void main(String args[]) {
		Map ysMap = FileZipUtil.fileToZip("E:\\upload\\cfp-server","E:\\upload\\cfp-server.zip");
		System.out.println("压缩结果："+ysMap);
		Map jyMap = FileZipUtil.zipToFile("E:\\upload\\cfp-server.zip","E:\\upload\\cfp-server2");
		System.out.println("解压结果："+jyMap);
	}

}
